#include <poll.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/socket.h>

#include <log4cxx/logger.h>

#include <iostream>

#include "SocketHelper.h"
#include "SocketConnPtr.h"

using std::cout;
using std::endl;


using namespace log4cxx;

static LoggerPtr g_logger = Logger::getLogger("SocketHelper");

int recv_all(const int fd, void * buf, const int len, const int timeout) {
	if (fd < 1 || NULL == buf || len <  0) {
		LOG4CXX_ERROR(g_logger, "recv all error, invalid args");
		return -1;
	}
	int got = 0;
	int t = -1;
	if (timeout > 0) t = timeout * 1000;
	while (got < len) {
		struct pollfd p;
		bzero(&p, sizeof(p));
		p.fd = fd;
		p.events = POLLIN;
		int ret = poll(&p, 1, t);
		if (ret < 0) {
			LOG4CXX_ERROR(g_logger, "in recv all poll error, ret:" << ret << ", errorno:" << errno);
			return -1;
		}
		if (0 == ret) {
			LOG4CXX_ERROR(g_logger, "in recv all poll timeout on fd:" << fd);
			return 0;
		}
		if (POLLIN != p.revents) {
			LOG4CXX_ERROR(g_logger, "in recv all poll found socket error, fd:" << fd << ", revents:" << p.revents);
			return -2;
		}
		ssize_t g = recv(fd, ((char *)buf) + got, len - got, 0);
		if (0 == g) {
			LOG4CXX_ERROR(g_logger, "in recv all socket closed by peer, fd:" << fd);
			return -3;
		}
		if (g < 0) {
			if (EAGAIN == errno || EINTR == errno) continue;
			LOG4CXX_ERROR(g_logger, "recv error, fd:" << fd << ", ret:" << g << ", errno:" << errno);
			return -4;
		}
		got += g;
	}
	return 1;
}

int recv_all(SocketConnPtr & conn, void * buf, const int len, const int timeout) {
	int fd = conn.get();
	if (fd < 0) return -100;
	int ret = recv_all(fd, buf, len, timeout);
	if (ret < 0) conn.set_network_error(true);
	return ret;
}

int send_all(int fd, const void * buf, int len, int timeout) {
	if (fd < 1 || NULL == buf || len <  0) {
		LOG4CXX_ERROR(g_logger, "recv all error, invalid args");
		return -1;
	}
	int sent = 0;
	int t = -1;
	if (timeout > 0) t = timeout * 1000;
	while (sent < len) {
		struct pollfd p;
		bzero(&p, sizeof(p));
		p.fd = fd;
		p.events = POLLOUT;
		int ret = poll(&p, 1, t);
		if (ret < 0) {
			LOG4CXX_ERROR(g_logger, "in send_all, poll error, ret:" << ret << ", errorno:" << errno);
			return -1;
		}
		if (0 == ret) {
			LOG4CXX_ERROR(g_logger, "in send_all poll timeout on fd:" << fd);
			return 0;
		}
		if (POLLOUT != p.revents) {
			LOG4CXX_ERROR(g_logger, "in send_all poll found socket error, fd:" << fd << ", revents:" << p.revents);
			return -2;
		}
		ssize_t g = send(fd, ((char *)buf) + sent, len - sent, 0);
		if (g <= 0) {
			if (EAGAIN == errno || EINTR == errno || EWOULDBLOCK == errno) continue;
			LOG4CXX_ERROR(g_logger, "send error, fd:" << fd << ", ret:" << g << ", errno:" << errno);
			return -4;
		}
		sent += g;
	}
	return 1;
}

int send_all(SocketConnPtr & conn, const void * buf, int len, int timeout) {
	int fd = conn.get();
	if (fd < 0) return -100;
	int ret = send_all(fd, buf, len, timeout);
	if (ret < 0) conn.set_network_error(true);
	return ret;
}
